
/* This program converts a string in form "p[.q[Er]]" into corresponding number.
   Here, p, q, r are integers (may be zero also). 'p' and 'r' may contain '+' or '-' signs.
   'E' may be 'e' also.

   It maintains a precision untill 15th decimal digit.
*/

# include <string.h>
# include <math.h>
# include <stdio.h>
# include <stdlib.h>

/* "ULONG_DIGITS <= maximum number of digits that an 'unsigned long' data-type (defined in 'limits.h') can hold". */
#define  ULONG_DIGITS  8
extern char **parse_str(const char *str, char c, unsigned short start, unsigned short end, unsigned short *nsubs);

static unsigned long str2int(const char *str);
static double str2double(const char *str);

extern double str2num(const char *str, short nsubs)
 {
  short lstr, ipos, epos, ptpos, tipos, n, sslen, _sslen, index;
  char *s, eEflag, ptflag;
  double num;

//    printf("Enteing into str2num()\n");

  lstr = strlen(str);

  if( (str[0] == '-') || (str[0] == '+')) ipos = 1;
  else ipos = 0;

  for(ptflag = eEflag = '\0', n = 0; n < lstr; n++)
     {
      if(str[n] == '.')
         {
          ptpos = n;
          ptflag = '.';
         }

      if( (str[n] == 'e') || (str[n] == 'E'))
          {
           eEflag = str[n];
           epos = n;
           break;
          }
     }

  if(eEflag == '\0') epos = lstr;
  if(ptflag == '\0') ptpos = epos;

  if(nsubs < 1) sslen = ULONG_DIGITS;
  else if( (sslen = (epos - ipos)/nsubs) < 1) sslen = 1;

  if(ptflag == '\0') n = ((sslen > lstr - epos + 1) ? sslen:lstr - epos + 1) + 1;
  else n = ((2*sslen > lstr - epos) ? 2*sslen:lstr - epos) + 2;

  s = (char *)malloc(n*sizeof(char));

  for(_sslen = -1, num = (double)0, tipos = ipos; tipos < epos; tipos += sslen)
    {
     if(ptflag != '\0') if(_sslen >= 0)
         {
          sslen = _sslen;
          _sslen = -1;
         }

     if(epos - tipos < sslen) sslen = epos - tipos;

     if(ptflag != '\0')
        {
         if((tipos < ptpos) && (ptpos <= tipos + sslen))
             {
              _sslen = sslen;
              sslen = 1 + ptpos - tipos + ((sslen <= epos - ptpos - 1) ? sslen : epos - ptpos - 1);
             }
       }

     strncpy(s, str + tipos, sslen);
     s[sslen] = '\0';

     if(tipos < ptpos)
          {
           if(tipos + sslen > ptpos) index = 0;
           else index = ptpos - tipos - sslen;
          }
     else index = ptpos - tipos - sslen + 1;

//      num += str2double(s)*powl(10, index);
     num += str2double(s)*pow(10, index);
    }

  if(eEflag != '\0')
     {
      strncpy(s+1, str + epos, lstr - epos);
      s[0] = '1';
      s[lstr - epos + 1] = '\0';

      num *= str2double(s);
     }

  if(ipos == 1) if(str[0] == '-') num *= -1;

  free(s);
// printf("...leaving str2num()\n");
  return num;
 }



static double str2double(const char *str)
{
 char **sstr, **rstr, c, flag, *s;

 unsigned long numl, numr;
 short i, k, index;
 unsigned short numr_len, nsubs;
 double num;

// printf("Enteing into str2double()\n");

 if(str[0] == '-')
     {
      flag = '-';
      k = 1;
     }
 else{
      flag = '+';
      if(str[0] == '+') k = 1;
      else k = 0;
     }

 sstr = parse_str(str, '.', 1, 2, &nsubs);

 if(nsubs == 2)
     {
      numl = str2int(k + *sstr);
      s = sstr[1];
      free(sstr[0]);
     }
 else if(nsubs == 1)
     {
      s = *sstr + k;
      numr = 0;
      numr_len = 1;
     }

 for(c = '\0', k = strlen(s), i = 0; i < k; i++)
   if( (s[i] == 'e') || (s[i] == 'E') )
        {
         c = s[i];
         break;
        }

 if(c == '\0')
     {
      if(nsubs == 2)
          {
           numr = str2int(s);
           numr_len = strlen(s);
           free(sstr[1]);
          }
      else{
           numl = str2int(s);
           free(sstr[0]);
          }

      free(sstr);
      index = 0;
     }
 else{
      rstr = parse_str(s, c, 1, 2, (unsigned short *)0);

      if(nsubs == 1)
          {
           free(sstr[0]);
           numl = str2int(rstr[0]);
          }
      else if(nsubs == 2)
          {
           free(sstr[1]);
           numr = str2int(rstr[0]);
           numr_len = strlen(rstr[0]);
          }
      free(rstr[0]);
      free(sstr);

      if(rstr[1][0] == '-') index = -1*str2int(1 + *(rstr+1));
      else{
           if(rstr[1][0] == '+') k = 1;
           else k = 0;

           index = str2int(k + *(rstr+1));
          }
      free(rstr[1]);
      free(rstr);
     }

 num = ((double)numl + (double)numr/pow(10, numr_len))*pow(10, index);
 if(flag == '-') num = -1*num;
 //printf("...leaving str2double()\n");
 return num;
}


static unsigned long str2int(const char *str)
  {
   int i, l;
   unsigned long n;
//printf("in str2int()\n");
   for(n = 0, l = strlen(str), i = 0; i < l; i++) n += ((short)(str[i]) - 48)*pow(10, l-i-1);
//printf("...leaving str2int()\n");
   return n;
  }
